package com.yixun.qfbao.interceptor.forbidrepeatclick;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.yixun.qfbao.constant.RedisKeyConstant;
import com.yixun.qfbao.interceptor.forbidrepeatclick.response.ResponseFactory;
import com.yixun.qfbao.util.RedisUtil;


/**
 * @author zilong.liu
 * @date 2020-11-23
 *
 * desc:
 *	防止重复点击     
 *	使用时需注意，原方法的返回值是否已经再枚举 ResponseTypeEnum 中定义，如果未定义，则需要自行扩展
 */
@Component
@Aspect
@Order(1)
public class ForbidRepeatClickInterceptor {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(ForbidRepeatClickInterceptor.class);
	
	@Pointcut("@annotation(com.yixun.qfbao.annotations.ForbidRepeatClick)")
    public void pointcut() {
    }
	
	@Around("pointcut()")
    public Object forbidRepeatClick(ProceedingJoinPoint pjp){
		//1、根据入参方法名获取组装的redis的key值
		String redisKey = getRedisKey(pjp);
		LOGGER.info("ForbidRepeatClickInterceptor->forbidRepeatClick->redisKey:{}", redisKey);
		
		if(RedisUtil.setIfAbsent(redisKey, "exist")) {
			LOGGER.info("ForbidRepeatClickInterceptor->forbidRepeatClick->redisKey:not exist");
			//2、当前方法同一时间段无完全同参数调用，则继续往下执行
			Object res;
			try {
				res = pjp.proceed();
			} catch (Throwable e) {
				LOGGER.error(e.getMessage(), e);
				res = this.getExceceptionResObj(pjp, e.getMessage());
			}
			//2.1 执行后将数据从redis删除
			RedisUtil.delete(redisKey);
			return res;
		}
		
		//3、当前方法同一时间段具有相同参数执行，则不再执行，直接返回错误标识
		LOGGER.info("ForbidRepeatClickInterceptor->forbidRepeatClick->redisKey:exist");
		return this.getResObj(pjp);
    }
	
	/**
	 * 获取存储的redis的key值
	 * @param pjp
	 * @return
	 */
	private String getRedisKey(ProceedingJoinPoint pjp) {
		
		// 1、获取被代理的对象类型
		String className = pjp.getTarget().getClass().getName();
		
		// 2、获取当前代理的方法名
		String methodName = pjp.getSignature().getName();
		
		// 3、获取入参并转换成json串
		String convertJson = convertArgsToJson(pjp.getArgs());
		
		String redisKey = RedisKeyConstant.ForbidRepeatClick.FORBID_REPEAT_CLICK +
				className + "->" + methodName + "->" + convertJson;
		
		return redisKey;
	}
	
	/**
	 * 将传入的参数拼接成json类型的字符串
	 * @param args
	 * @return
	 */
	private String convertArgsToJson(Object[] args) {
		StringBuilder convertJson = new StringBuilder();
		for (Object object : args) {
			if(!(object instanceof HttpServletRequest)) {
				convertJson.append(JSON.toJSONString(object));
			}
		}
		return convertJson.toString();
	}
	
	/**
	 * 当重复点击时，获取固定的返回值
	 * @param pjp
	 * @return
	 */
	private Object getResObj(ProceedingJoinPoint pjp) {
		String returnType = getReturnType(pjp);
		return ResponseFactory.getResTypeResponse(returnType).getResObj();
	}
	
	/**
	 * 当重复点击时，获取固定的返回值
	 * @param pjp
	 * @return
	 */
	private Object getExceceptionResObj(ProceedingJoinPoint pjp, String errMsg) {
		String returnType = getReturnType(pjp);
		return ResponseFactory.getResTypeResponse(returnType).getExceptionResObj(errMsg);
	}
	
	/**
	 * 获取返回值的全限定名
	 * @param pjp
	 * @return
	 */
	private String getReturnType(ProceedingJoinPoint pjp) {
		MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
		Class<?> resType = methodSignature.getReturnType();
		String returnType = resType.getName();
		return returnType;
	}
	
}
